package cn.bugstack.springframework.aop.framework.autoproxy;

import cn.bugstack.springframework.aop.*;
import cn.bugstack.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import cn.bugstack.springframework.aop.framework.ProxyFactory;
import cn.bugstack.springframework.beans.BeansException;
import cn.bugstack.springframework.beans.PropertyValues;
import cn.bugstack.springframework.beans.factory.BeanFactory;
import cn.bugstack.springframework.beans.factory.BeanFactoryAware;
import cn.bugstack.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import cn.bugstack.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.util.Collection;

/**
 * `DefaultAdvisorAutoProxyCreator` 是 Spring 框架中用于创建基于切面（Advisor）的自动代理的一个类。
 * 它实现了 `InstantiationAwareBeanPostProcessor` 和 `BeanFactoryAware` 接口，
 * 从而具备了在 Spring 容器中创建 bean 的时候自动创建代理对象的能力。
 *
 * 下面是对 `DefaultAdvisorAutoProxyCreator` 类的解析：
 * 1. `InstantiationAwareBeanPostProcessor` 接口：
 *     通过实现这个接口，`DefaultAdvisorAutoProxyCreator` 能够在 bean 实例化前后进行操作。
 *     在 `postProcessBeforeInstantiation` 方法中，它可以决定是否为特定的 bean 创建一个代理对象。
 *     在 `postProcessAfterInitialization` 方法中，它可以在 bean 初始化之后对代理进行进一步的配置。
 * 2. `BeanFactoryAware` 接口：实现这个接口使得 `DefaultAdvisorAutoProxyCreator` 能够获取到它所在的 `BeanFactory` 的引用。
 *     这样，它就可以在创建代理的时候访问 `BeanFactory` 中定义的切面和其他 bean。
 *
 * `DefaultAdvisorAutoProxyCreator` 的工作原理是，它会在 Spring 容器中查找所有的切面（Advisor），
 * 然后根据这些切面的定义来为匹配的 bean 创建代理。切面通常包含一个切入点（Pointcut）和一个通知（Advice），当 bean 的方法执行匹配到切入点时，就会触发通知的逻辑。
 *
 * 通过使用 `DefaultAdvisorAutoProxyCreator`，开发者可以非常方便地实现 AOP（面向切面编程）功能，而无需手动为每个 bean 创建代理。
 * 这使得 Spring 的 AOP 功能更加易用，并且能够与 Spring 的 IoC 容器紧密集成。
 */
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    // 通过BeanFactory感知容器获取到的beanFactory对象
    private DefaultListableBeanFactory beanFactory;


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    /**
     * 这段代码的作用是在目标 bean 实例化之前检查是否有与之匹配的切面 Advisor，并根据需要创建代理对象。
     * 如果存在匹配的切面 Advisor，则将会创建一个代理对象，并将切面的功能织入到目标 bean 中，实现面向切面编程（AOP）的功能。
     */
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
/*
        // 首先，通过 isInfrastructureClass 方法判断目标 bean 类是否是基础设施类（如 Advice、Pointcut、Advisor 等），如果是则直接返回 null，不做处理。
        if (isInfrastructureClass(beanClass)) return null;


        // 获取应用上下文中所有的 AspectJExpressionPointcutAdvisor 类型的 bean，这些 bean 将用于切面功能。
        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();

        // 遍历所有的切面 Advisor，逐个判断它们的切点是否匹配当前的 bean 类。如果匹配，则为当前的 bean 创建代理对象。
        for (AspectJExpressionPointcutAdvisor advisor : advisors) {

            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            if (!classFilter.matches(beanClass)) continue;
            // 创建代理对象的过程中，使用 AdvisedSupport 和 ProxyFactory 来配置和创建代理对象，将切面 Advisor 的拦截器和切点设置到代理对象中。
            AdvisedSupport advisedSupport = new AdvisedSupport();

            TargetSource targetSource = null;
            try {
                targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
            } catch (Exception e) {
                e.printStackTrace();
            }
            advisedSupport.setTargetSource(targetSource);
            // 返回的是一个MethodBeforeAdviceInterceptor对象
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            // 返回的是一个AspectJExpressionPointcut对象
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(false);

            return new ProxyFactory(advisedSupport).getProxy();

        }
        // 如果没有匹配到任何切面 Advisor，则返回 null，表示使用原始的目标 bean 实例。
        return null;
 */
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    /**
     * 这段代码是一个方法，用于判断给定的类是否为基础设施类（Infrastructure Class）。
     * 在 Spring AOP 中，基础设施类通常是与切面相关的类，如通知（Advice）、切点（Pointcut）和顾问（Advisor）。
     * isInfrastructureClass 方法接收一个 Class<?> 类型的参数 beanClass，表示要检查的类。
     * 方法通过判断给定的类是否是 Advice 类的子类、Pointcut 类的子类或者 Advisor 类的子类来确定是否为基础设施类。
     * 如果给定的类是上述类之一的子类，方法将返回 true，否则返回 false。
     * 这个方法通常用于 Spring AOP 的 BeanPostProcessor 实现中，用于过滤基础设施类，以便在 Bean 创建过程中应用 AOP 相关的逻辑。
     */

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (isInfrastructureClass(bean.getClass())) return bean;

        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();

        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            // 过滤匹配类
            if (!classFilter.matches(bean.getClass())) continue;

            AdvisedSupport advisedSupport = new AdvisedSupport();

            TargetSource targetSource = new TargetSource(bean);
            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(false);

            // 返回代理对象
            return new ProxyFactory(advisedSupport).getProxy();
        }

        return bean;
    }

    private boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        return pvs;
    }
    
}
